#include "ASWaiting.h"

#define AS_MAX_WAITING_CTRLS 10
#define AS_WAIT_MAX_POINTS   10
#define CIRCLE_RADIUS 5

typedef struct
{
    double x_rate, y_rate;
} point_rate;

/*
 * theta = 360/AS_WAIT_MAX_POINTS  * i;
 * sin  and  cos
 */
static point_rate s_as_waiting_default_rate[AS_WAIT_MAX_POINTS] =
{
    {0, 1}, {0.588, 0.809}, {0.951, 0.309}, {0.951, -0.309}, {0.588, -0.809},
    {0, -1}, { -0.588, -0.809}, { -0.951, -0.309}, { -0.951, 0.309}, { -0.588, 0.809}
};


static as_point s_as_ctrl_waiting_default_pos[AS_WAIT_MAX_POINTS];
static ASUINT s_as_ctrl_waiting_default_color[AS_WAIT_MAX_POINTS] =
{
    0x000000, 0x333333, 0x666666, 0x999999, 0xcccccc, 0xcccccc, 0xcccccc, 0xcccccc, 0xcccccc, 0xcccccc
};

//this array store all created as_waiting ctrls ,just for use functions about timer.
static as_waiting* s_as_waiting_ctrl_store[AS_MAX_WAITING_CTRLS];

static void as_ctrl_waiting_init_pos_type_default(as_waiting* ctrl_waiting_p)
{
    ASINT i = 0;
    ASINT16 d, r, x, y;

    if(!ctrl_waiting_p)
        return;

    d = AS_MIN(ctrl_waiting_p->base.area.w, ctrl_waiting_p->base.area.h);
    r = (d >> 1) - CIRCLE_RADIUS;
    x = ctrl_waiting_p->base.area.x + (ctrl_waiting_p->base.area.w - d) / 2 + r + CIRCLE_RADIUS;
    y = ctrl_waiting_p->base.area.y + (ctrl_waiting_p->base.area.h - d) / 2 + r + CIRCLE_RADIUS;

    for (i = 0; i < AS_WAIT_MAX_POINTS; i++)
    {
        s_as_ctrl_waiting_default_pos[i].x = x + (ASINT16)(r * s_as_waiting_default_rate[i].x_rate);
        s_as_ctrl_waiting_default_pos[i].y = y + (ASINT16)(r * s_as_waiting_default_rate[i].y_rate);
    }
}

static void as_ctrl_waiting_init_pos_type_line(as_waiting* ctrl_waiting_p)
{
    ASINT i = 0;
    ASINT gap = 0, y_strat;
    as_rect* area_p = NULL;

    if(!ctrl_waiting_p)
        return;

    area_p = &ctrl_waiting_p->base.area;
    gap = (area_p->w - AS_WAIT_MAX_POINTS * CIRCLE_RADIUS * 2) / (AS_WAIT_MAX_POINTS - 1);

    if(gap <= 0)
        return;

    y_strat = area_p->y + ((area_p->h - CIRCLE_RADIUS * 2) >> 1);

    for (i = 0; i < AS_WAIT_MAX_POINTS; i++)
    {
        s_as_ctrl_waiting_default_pos[i].x = (ASINT16)(area_p->x + i * (gap + CIRCLE_RADIUS * 2));
        s_as_ctrl_waiting_default_pos[i].y = (ASINT16)y_strat;
    }
}

static void as_ctrl_waiting_init_pos_by_type(as_waiting* ctrl_waiting_p)
{
    if(!ctrl_waiting_p)
        return;

    switch(ctrl_waiting_p->type)
    {
        case AS_WAITING_TYPE_DEFAULT:
            as_ctrl_waiting_init_pos_type_default(ctrl_waiting_p);
            break;

        case AS_WAITING_TYPE_LINE:
            as_ctrl_waiting_init_pos_type_line(ctrl_waiting_p);
            break;

        default:
            break;
    }
}

static void as_ctrl_waiting_paint_type_default(as_control* ctrl_p)
{
    ASINT i = 0, layer;
    ASUINT idx, tmp_idx;
    as_waiting* ctrl_waiting_p = (as_waiting*)ctrl_p;
    as_rect* area_p;
    ASUINT8* paint_buf;

    if(!ctrl_waiting_p || ctrl_waiting_p->base.if_hidden == TRUE)
        return;

    layer = ctrl_p->show_hdlr;
    paint_buf = as_get_layer_buffer_by_hdlr(layer);
    area_p = &ctrl_p->area;

    if(layer < 0 || as_set_layer_clip_ex(layer, area_p) == AS_IGNORE
       || as_set_layer_clip_ex(layer, &ctrl_waiting_p->valid_area) == AS_IGNORE)
        return;

    if(!ctrl_waiting_p->bg_hidden)
    {
        //fill bg
        as_fill_rect(area_p, ctrl_waiting_p->bg_color, ctrl_waiting_p->bg_color);
    }

    //draw circles
    idx = ctrl_waiting_p->start_idx;

    for(i = /*0*/AS_WAIT_MAX_POINTS >> 1; i < AS_WAIT_MAX_POINTS; i++)
    {
        tmp_idx = idx + i;

        if(tmp_idx >= AS_WAIT_MAX_POINTS)
        {
            tmp_idx -= AS_WAIT_MAX_POINTS;
        }

        //for clockwise
        tmp_idx = (AS_WAIT_MAX_POINTS - 1) - tmp_idx;
        as_fill_circle(s_as_ctrl_waiting_default_pos[tmp_idx].x - CIRCLE_RADIUS,
                       s_as_ctrl_waiting_default_pos[tmp_idx].y - CIRCLE_RADIUS,
                       CIRCLE_RADIUS, s_as_ctrl_waiting_default_color[AS_WAIT_MAX_POINTS - i]);
    }

    as_reset_layer_clip(layer);
    ctrl_waiting_p->start_idx++;

    if(ctrl_waiting_p->start_idx >= AS_WAIT_MAX_POINTS)
        ctrl_waiting_p->start_idx = 0;
}


static void as_ctrl_waiting_paint(as_control* ctrl_p)
{
    as_waiting* wait_p = NULL;

    if(!ctrl_p || ctrl_p->if_hidden)
        return;

    wait_p = (as_waiting*)ctrl_p;

    switch (wait_p->type)
    {
        case AS_WAITING_TYPE_DEFAULT:
        case AS_WAITING_TYPE_LINE:
            as_ctrl_waiting_paint_type_default(ctrl_p);
            break;

        default:
            //warn here
            break;
    }
}

static void as_ctrl_waiting_timer_callback(ASINT tid)
{
    ASINT i = 0;

    for (i = 0; i < AS_MAX_WAITING_CTRLS; i++)
    {
        if(s_as_waiting_ctrl_store[i]/* && s_as_waiting_ctrl_store[i]->timer_id == tid*/)
        {
            s_as_waiting_ctrl_store[i]->base.paint_hdlr((as_control*)s_as_waiting_ctrl_store[i]);
            as_ctrl_flush();
            break;
        }
    }
}

static as_ret as_ctrl_waiting_store_ptr(as_waiting* ctrl_waiting_p)
{
    ASINT i = 0;
    as_ret ret = AS_OK;

    if(!ctrl_waiting_p)
    {
        //warning
        ret = AS_INVALID_PARAM;
        return ret;
    }

    for(i = 0; i < AS_MAX_WAITING_CTRLS; i++)
    {
        if(s_as_waiting_ctrl_store[i] == NULL)
        {
            s_as_waiting_ctrl_store[i] = ctrl_waiting_p;
            break;
        }
    }

    if(i == AS_MAX_WAITING_CTRLS)
    {
        //warning
        ret = AS_MEMORY_NOT_ENOUGH;
        return ret;
    }

    return ret;
}

static void as_ctrl_waiting_remove_ptr(as_waiting* ctrl_waiting_p)
{
    ASINT i = 0;

    if(!ctrl_waiting_p)
    {
        //warning
        return;
    }

    for(i = 0; i < AS_MAX_WAITING_CTRLS; i++)
    {
        if(s_as_waiting_ctrl_store[i] == ctrl_waiting_p)
        {
            s_as_waiting_ctrl_store[i] = NULL;
            break;
        }
    }
}

static as_ret as_ctrl_waiting_change_period(as_control* ctrl_p, ASINT period)
{
    as_waiting* wait_p = NULL;
    as_ret ret = AS_OK;

    if(!ctrl_p || period < 50)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    wait_p = (as_waiting*)ctrl_p;

    if(wait_p->timer_id > 0)
    {
        as_destroy_timer(wait_p->timer_id);
    }

    wait_p->update_period = period;
    wait_p->timer_id = as_create_timer(wait_p->update_period, wait_p->timer_cb);
    return ret;
}

static as_ret as_ctrl_waiting_change_type(as_control* ctrl_p, as_waiting_type type)
{
    as_waiting* wait_p = NULL;
    as_ret ret = AS_OK;

    if(!ctrl_p || type < AS_WAITING_TYPE_DEFAULT || type >= AS_WAITING_TYPE_MAX)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    wait_p = (as_waiting*)ctrl_p;
    wait_p->type = type;
    //wait_p->base.paint_hdlr = as_ctrl_waiting_paint_type_default;
    return ret;
}

static as_ret as_ctrl_waiting_set_valid_area(as_control* ctrl_p, as_rect valid_area)
{
    as_waiting* wait_p = (as_waiting*)ctrl_p;

    if(!wait_p)
        return AS_INVALID_PARAM;

    as_memcpy(&wait_p->valid_area, &valid_area, sizeof(as_rect));
    return AS_OK;
}

static as_ret as_ctrl_waiting_change_bg_color(as_control* ctrl_p, ASUINT bg_color)
{
    as_waiting* wait_p = NULL;
    as_ret ret = AS_OK;

    if(!ctrl_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    wait_p = (as_waiting*)ctrl_p;
    wait_p->bg_color = bg_color;
    return ret;
}

static as_ret as_ctrl_waiting_set_bg_hidden(as_control* ctrl_p, ASINT if_hidden)
{
    as_waiting* wait_p = (as_waiting*)ctrl_p;

    if(!wait_p || wait_p->bg_hidden == if_hidden)
        return AS_INVALID_PARAM;

    wait_p->bg_hidden = if_hidden;
    return AS_OK;
}

static void as_ctrl_waiting_set_hidden(as_control* ctrl_p, ASINT if_hidden)
{
    as_waiting* wait_p = (as_waiting*)ctrl_p;

    if(!wait_p || wait_p->base.if_hidden == if_hidden)
        return;

    wait_p->base.if_hidden = if_hidden;

    if(if_hidden == ASTRUE)
    {
        if(wait_p->timer_id >= 0)
        {
            as_destroy_timer(wait_p->timer_id);
            wait_p->timer_id = -1;
        }
    }
    else
    {
        if(wait_p->timer_id < 0)
        {
            wait_p->timer_id = as_create_timer(wait_p->update_period, wait_p->timer_cb);
        }
    }
}

static void as_ctrl_waiting_move(as_control* ctrl_p, ASINT x_off, ASINT y_off)
{
    as_waiting* waiting_p = (as_waiting*)ctrl_p;

    if(!waiting_p)
        return;

    waiting_p->base.area.x += (ASINT16)x_off;
    waiting_p->base.area.y += (ASINT16)y_off;

    if(waiting_p->bg_img)
    {
        waiting_p->bg_img->base.move((as_control*)waiting_p->bg_img, x_off, y_off);
    }

    as_ctrl_waiting_init_pos_by_type(waiting_p);
}


as_waiting* as_ctrl_waiting_create(as_rect area, as_waiting_type type)
{
    as_waiting* wait_p = NULL;
    wait_p = as_calloc(sizeof(as_waiting));

    if(!wait_p)
    {
        return wait_p;
    }

    if(as_ctrl_waiting_store_ptr(wait_p) != AS_OK)
    {
        as_free(wait_p);
        wait_p = NULL;
        return wait_p;
    }

    as_ctrl_init((as_control*)wait_p, area);
    wait_p->base.paint_hdlr = as_ctrl_waiting_paint;
    wait_p->base.set_hidden = as_ctrl_waiting_set_hidden;
    wait_p->base.move = as_ctrl_waiting_move;
    wait_p->base.ctrl_type = AS_CTRL_WAITING;
    //if can not create timer, you will not create a waiting control successfully.
    wait_p->update_period = 200;
    wait_p->timer_cb = as_ctrl_waiting_timer_callback;
    wait_p->timer_id = as_create_timer(wait_p->update_period, wait_p->timer_cb);

    if(wait_p->timer_id < 0)
    {
        as_free(wait_p);
        wait_p = NULL;
        return wait_p;
    }

    wait_p->type = type;
    as_ctrl_waiting_init_pos_by_type(wait_p);
    wait_p->start_idx = 0;
    wait_p->bg_color = AS_COLOR_TRANS;
    wait_p->change_period = as_ctrl_waiting_change_period;
    //wait_p->change_type = as_ctrl_waiting_change_type;
    wait_p->change_bg_color = as_ctrl_waiting_change_bg_color;
    wait_p->set_bg_hidden = as_ctrl_waiting_set_bg_hidden;
    wait_p->set_valid_area = as_ctrl_waiting_set_valid_area;
    wait_p->set_valid_area((as_control*)wait_p, wait_p->base.area);
    return wait_p;
}

as_ret as_ctrl_waiting_destroy(as_waiting* ctrl_waiting_p)
{
    as_ret ret = AS_OK;

    if(!ctrl_waiting_p)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    if(ctrl_waiting_p->bg_img)
    {
        as_ctrl_destroy((as_control*)ctrl_waiting_p->bg_img);
        ctrl_waiting_p->bg_img = NULL;
    }

    as_destroy_timer(ctrl_waiting_p->timer_id);
    as_ctrl_waiting_remove_ptr(ctrl_waiting_p);
    as_free(ctrl_waiting_p);
    ctrl_waiting_p = NULL;
    return ret;
}